#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>

#include <config.h>

#ifdef YUN_TOUCH_SCREEN_SUPPORT

#include <types.h>
#include <gapi.h>
#include <event.h>
#include <drivers/input/input.h>
#include <drivers/input/touchscr.h>

extern int xres, yres;

struct rectify_param rectify_param = { 1, 0, 1, 0 };

struct touch_operations yun_ops = {
    .open = yun_open,
    .close = yun_close,
    .rectify = yun_rectify,
    .polling = yun_polling,
    .lock = yun_lock,
    .unlock = yun_unlock,
};

struct input_device yun_dev = {-1, &yun_ops, &rectify_param };
extern struct input_device *inp_dev;

int yun_open(void)
{
    int val = 1;
    
    yun_dev.dev_id = open(TSDEV, O_RDWR);
    if(yun_dev.dev_id < 0) {
        ERROR("Cannot open touch screen device\n");
        return yun_dev.dev_id;
    }
    ioctl(yun_dev.dev_id, TSIOCTL_DEVICE_CONTROL, &val);
    
    yun_draw_rectifier();
    
    return 0;
}

void yun_close(void)
{
    int val = 0;
    yun_set_int_mode(TS_INT_BOTH_DISABLE);
    ioctl(yun_dev.dev_id, TSIOCTL_DEVICE_CONTROL, &val);
    close(yun_dev.dev_id);
    yun_dev.dev_id = -1;
}

void yun_set_int_mode(u8 mask)
{
    u8 msk = mask; /* 20070509 */
    ioctl(yun_dev.dev_id, TSIOCTL_MASK_INTR, &msk);
}

void yun_polling(EVENT *e)
{
    if(e) {
        ioctl(yun_dev.dev_id, TSIOCTL_WAIT_EVENT, e);
        //DPRINTF("Before rectify: x = %d, y = %d\n", e->pos.x, e->pos.y);
        e->pos.x = rectify_param.X1 * e->pos.x + rectify_param.X2;
        e->pos.y = rectify_param.Y1 * e->pos.y + rectify_param.Y2;
        //DPRINTF("After rectify: x = %d, y = %d\n", e->pos.x, e->pos.y);
    }
}

COORD *yun_get_coordinate(void)
{
    COORD *cur = (COORD *)malloc(sizeof(COORD));
    
    ioctl(yun_dev.dev_id, TSIOCTL_GET_COORDINATE, cur);
    /* Rectifying with rectify parameters */
    cur->x = rectify_param.X1 * cur->x + rectify_param.X2;
    cur->y = rectify_param.Y1 * cur->y + rectify_param.Y2;
    
    return cur;
}

struct rectify_param yun_rectify_calc_param(COORD vis_lp, COORD vis_rp, COORD real_lp, COORD real_rp)
{
    struct rectify_param XY_ab;
    
    if(vis_rp.x <= vis_lp.x || vis_rp.y <= vis_lp.y ||
       real_rp.x <= real_lp.x || real_rp.y <= real_lp.y) {
        /* Restore default */
        XY_ab.X1 = 0;
        XY_ab.X2 = 0;
        XY_ab.Y1 = 0;
        XY_ab.Y2 = 0;
        return XY_ab;
    }
  
    /* Calculate parameter X */
    XY_ab.X1 = (float)(vis_rp.x - vis_lp.x) / (float)(real_rp.x - real_lp.x);
    XY_ab.X2 = vis_lp.x - ((float)(vis_rp.x - vis_lp.x)/(float)(real_rp.x - real_lp.x)) * real_lp.x;

    /* Calculate parameter Y */
    XY_ab.Y1 = (float)(vis_rp.y-vis_lp.y) / (float)(real_rp.y-real_lp.y);
    XY_ab.Y2 = vis_lp.y - ((float)(vis_rp.y - vis_lp.y)/(float)(real_rp.y - real_lp.y)) * real_lp.y;
    
    DPRINTF("X1: %f, X2: %f, Y1: %f, Y2: %f\n", XY_ab.X1, XY_ab.X2, XY_ab.Y1, XY_ab.Y2);
    
    return XY_ab;
}

void yun_draw_rectifier(void)
{
    RECT rect;
    DOT dot;
    EVENT event;
    COORD vis_lp, vis_rp, real_lp, real_rp;
    
    /* Initialize base coordinate for rectifying */
    vis_lp.x = 50;
    vis_lp.y = 50;
    vis_rp.x = 750;
    vis_rp.y = 430;
    
    /* Draw first dot at (50,50) */
    //DPRINTF("Drawing rectify background\n");
    rect.x = 0;
    rect.y = 0;
    rect.width = xres;
    rect.height = yres;
    rect.color = pCOLOR_BLACK;
#ifdef GPU_SUPPORT
    rect.hw_dep_prop.command = 0;
    rect.hw_dep_prop.end_layer = false;
    rect.hw_dep_prop.scale = 0;
    rect.hw_dep_prop.angle = 0;
#endif
    rect.filled = true;
    rect.tp = false;
    DrawRect(&rect);
    dot.x = vis_lp.x - 10;
    dot.y = vis_lp.y - 10;
    dot.size = 20;
    dot.color = pCOLOR_WHITE;
#ifdef GPU_SUPPORT
    dot.hw_dep_prop.command = 0;
    dot.hw_dep_prop.end_layer = false;
    dot.hw_dep_prop.scale = 0;
    dot.hw_dep_prop.angle = 0;
#endif
    dot.tp = false;
    DrawDot(&dot);
    dot.x = vis_rp.x - 10;
    dot.y = vis_rp.y - 10;
    dot.size = 20;
    dot.color = pCOLOR_WHITE;
#ifdef GPU_SUPPORT
    dot.hw_dep_prop.command = 0;
    dot.hw_dep_prop.end_layer = true;
    dot.hw_dep_prop.scale = 0;
    dot.hw_dep_prop.angle = 0;
#endif
    dot.tp = false;
    DrawDot(&dot);

    /* Enable press interrupt only */
    yun_set_int_mode(TS_INT_PRESS_ONLY);
    /* Wait for first touch */
    printf("waiting for first touch\n");
    yun_polling(&event);
    //ts_get_coordinate(&real_lp);
    real_lp.x = event.pos.x;
    real_lp.y = event.pos.y;
    
    /* Wait for second touch */
    printf("waiting for second touch\n");
    yun_polling(&event);
    //ts_get_coordinate(&real_rp);
    real_rp.x = event.pos.x;
    real_rp.y = event.pos.y;
    
    yun_rectify(vis_lp, vis_rp, real_lp, real_rp);
}

void yun_rectify(COORD vis_lp, COORD vis_rp, COORD real_lp, COORD real_rp)
{
    if(yun_dev.dev_id < 0) {
        ERROR("Unknown touch device!\n");
        return;
    }
       
    /* Disable all interrupt of touch screen for a while */
    yun_set_int_mode(TS_INT_BOTH_DISABLE);
        
    rectify_param = yun_rectify_calc_param(vis_lp, vis_rp, real_lp, real_rp);
    if(rectify_param.X1 == 0 && rectify_param.X2 == 0) {
        WARNING("Touch screen rectify failed!\nUsing default rectify parameter\n");
        rectify_param.X1 = X_A;
        rectify_param.X2 = X_B;
        rectify_param.Y1 = Y_A;
        rectify_param.Y2 = Y_B;
    }
    
    /* Re-enable interrupt */
    yun_set_int_mode(TS_INT_BOTH_ENABLE);
}

void yun_lock()
{
    int val = 0;
    ioctl(yun_dev.dev_id, TSIOCTL_DEVICE_CONTROL, &val);
}

void yun_unlock()
{
    int val = 1;
    ioctl(yun_dev.dev_id, TSIOCTL_DEVICE_CONTROL, &val);
}

int yun_init(void)
{
    int ret = 0;
    inp_dev = &yun_dev;
    
    if(inp_dev->touch_ops->open)
        ret = inp_dev->touch_ops->open();
        
    return ret;
}

void yun_exit(void)
{
    if(inp_dev->touch_ops->close)
        inp_dev->touch_ops->close();
    inp_dev = NULL;
}

#endif /* YUN_TOUCH_SCREEN_SUPPORT */
